#ifndef _FILE_H_
#define _FILE_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct file_t{
    void *user_data;
    void (*close)(void*user_data);
    size_t (*write)(void*data,size_t _ElementSize,size_t count,void*user_data);
    size_t (*read)(void*data,size_t _ElementSize,size_t count,void*user_data);
    int (*seek)(void*data,long pos,int where);
    long (*tell)(void*data);
    const char* type;
}file_t;

static void file_close(file_t*file){
    file->close(file->user_data);
    free(file);
}

static void* file_userdata(file_t*file){
    return file->user_data;
}

static size_t file_seek(file_t*file,long pos,int Origin){
    return file->seek(file->user_data,pos,Origin);
}

static int file_write(void*data,int _ElementSize,int c,file_t*file){
    return file->write(data,_ElementSize,c,file->user_data);
}

static int file_read(void*data,int _ElementSize,int c,file_t*file){
    return file->read(data,_ElementSize,c,file->user_data);
}

static size_t file_tell(file_t*file){
    return file->tell(file->user_data);
}

static int file_is(file_t*file,const char*type){
    return strcmp(type,file->type)==0;
}

static void* file_get_user_data(file_t*file){
    return file->user_data;
}

static file_t* file_open(const char*file_path,const char*mode){
    FILE*file = fopen(file_path,mode);
    file_t *f = (file_t*)calloc(1,sizeof(*f));
    *(void**)&f->close = (void*)fclose;
    *(void**)&f->read  = (void*)fread;
    *(void**)&f->seek  = (void*)fseek;
    *(void**)&f->tell  = (void*)ftell;
    *(void**)&f->write = (void*)fwrite;
    f->type = "FILE";
    f->user_data = (void*)file;
    return f;
}

static file_t* file_new(void*user_data,
    const char*type,
    void (*close)(void*user_data),
    size_t (*write)(void*data,size_t _ElementSize,size_t count,void*user_data),
    size_t (*read)(void*data,size_t _ElementSize,size_t count,void*user_data),
    int (*seek)(void*data,long pos,int where),
    long (*tell)(void*data)
){
    file_t *f = (file_t*)calloc(1,sizeof(*f));
    f->user_data = user_data;
    f->type = type;
    f->close = close;
    f->read = read;
    f->write = write;
    f->seek = seek;
    f->tell = tell;
    return f;
}

#endif
